//===--- Demangle.h - Interface to Swift symbol demangling ------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_BASIC_DEMANGLE_H
#define SWIFT_BASIC_DEMANGLE_H

#include <memory>
#include <string>
#include <vector>
#include <cassert>
#include <cstdint>
//#include "llvm/ADT/StringRef.h"
//#include "swift/Basic/Malloc.h"
#include "StringRef.h"
#include "Malloc.h"

namespace llvm {
class raw_ostream;
}

namespace swift {
namespace Demangle {

struct DemangleOptions {
    bool SynthesizeSugarOnTypes = false;
    bool DisplayTypeOfIVarFieldOffset = true;
    bool DisplayDebuggerGeneratedModule = true;
    bool QualifyEntities = true;
    bool DisplayExtensionContexts = true;
    bool DisplayUnmangledSuffix = true;
    bool DisplayModuleNames = true;
    bool DisplayGenericSpecializations = true;
    bool DisplayProtocolConformances = true;
    bool DisplayWhereClauses = true;
    bool DisplayEntityTypes = true;
    bool ShortenPartialApply = false;
    bool ShortenThunk = false;
    bool ShortenValueWitness = false;
    bool ShortenArchetype = false;
    bool ShowPrivateDiscriminators = true;

    DemangleOptions() {}

    static DemangleOptions SimplifiedUIDemangleOptions() {
        auto Opt = DemangleOptions();
        Opt.SynthesizeSugarOnTypes = true;
        Opt.QualifyEntities = true;
        Opt.DisplayExtensionContexts = false;
        Opt.DisplayUnmangledSuffix = false;
        Opt.DisplayModuleNames = false;
        Opt.DisplayGenericSpecializations = false;
        Opt.DisplayProtocolConformances = false;
        Opt.DisplayWhereClauses = false;
        Opt.DisplayEntityTypes = false;
        Opt.ShortenPartialApply = true;
        Opt.ShortenThunk = true;
        Opt.ShortenValueWitness = true;
        Opt.ShortenArchetype = true;
        Opt.ShowPrivateDiscriminators = false;
        return Opt;
    };
};

class Node;
typedef std::shared_ptr<Node> NodePointer;

enum class FunctionSigSpecializationParamKind : unsigned {
    // Option Flags use bits 0-5. This give us 6 bits implying 64 entries to
    // work with.
    ConstantPropFunction = 0,
    ConstantPropGlobal = 1,
    ConstantPropInteger = 2,
    ConstantPropFloat = 3,
    ConstantPropString = 4,
    ClosureProp = 5,
    BoxToValue = 6,
    BoxToStack = 7,

    // Option Set Flags use bits 6-31. This gives us 26 bits to use for option
    // flags.
    Dead = 1 << 6,
    OwnedToGuaranteed = 1 << 7,
    SROA = 1 << 8,
};

/// The pass that caused the specialization to occur. We use this to make sure
/// that two passes that generate similar changes do not yield the same
/// mangling. This currently cannot happen, so this is just a safety measure
/// that creates separate name spaces.
enum class SpecializationPass : uint8_t {
    AllocBoxToStack,
    ClosureSpecializer,
    CapturePromotion,
    CapturePropagation,
    FunctionSignatureOpts,
    GenericSpecializer,
};

static inline char encodeSpecializationPass(SpecializationPass Pass) {
    return char(uint8_t(Pass)) + '0';
}

enum class ValueWitnessKind {
    AllocateBuffer,
    AssignWithCopy,
    AssignWithTake,
    DeallocateBuffer,
    Destroy,
    DestroyBuffer,
    InitializeBufferWithCopyOfBuffer,
    InitializeBufferWithCopy,
    InitializeWithCopy,
    InitializeBufferWithTake,
    InitializeWithTake,
    ProjectBuffer,
    InitializeBufferWithTakeOfBuffer,
    DestroyArray,
    InitializeArrayWithCopy,
    InitializeArrayWithTakeFrontToBack,
    InitializeArrayWithTakeBackToFront,
    StoreExtraInhabitant,
    GetExtraInhabitantIndex,
    GetEnumTag,
    DestructiveProjectEnumData,
};

enum class Directness { Direct, Indirect };

class Node : public std::enable_shared_from_this<Node> {
public:
    enum class Kind : uint16_t {
#define NODE(ID) ID,
//#include "swift/Basic/DemangleNodes.def"
#include "DemangleNodes.h"
    };

    typedef uint64_t IndexType;

private:
    Kind NodeKind;

    enum class PayloadKind : uint8_t { None, Text, Index };
    PayloadKind NodePayloadKind;

    union {
        std::string TextPayload;
        IndexType IndexPayload;
    };

    // FIXME: use allocator.
    typedef std::vector<NodePointer> NodeVector;
    NodeVector Children;

    Node(Kind k) : NodeKind(k), NodePayloadKind(PayloadKind::None) {}
    Node(Kind k, std::string &&t) : NodeKind(k), NodePayloadKind(PayloadKind::Text) { new (&TextPayload) std::string(std::move(t)); }
    Node(Kind k, IndexType index) : NodeKind(k), NodePayloadKind(PayloadKind::Index) { IndexPayload = index; }
    Node(const Node &) = delete;
    Node &operator=(const Node &) = delete;

    friend struct NodeFactory;

public:
    ~Node();

    Kind getKind() const { return NodeKind; }

    bool hasText() const { return NodePayloadKind == PayloadKind::Text; }
    const std::string &getText() const {
        assert(hasText());
        return TextPayload;
    }

    bool hasIndex() const { return NodePayloadKind == PayloadKind::Index; }
    uint64_t getIndex() const {
        assert(hasIndex());
        return IndexPayload;
    }

    typedef NodeVector::iterator iterator;
    typedef NodeVector::const_iterator const_iterator;
    typedef NodeVector::size_type size_type;

    bool hasChildren() const { return !Children.empty(); }
    size_t getNumChildren() const { return Children.size(); }
    iterator begin() { return Children.begin(); }
    iterator end() { return Children.end(); }
    const_iterator begin() const { return Children.begin(); }
    const_iterator end() const { return Children.end(); }

    NodePointer getFirstChild() const { return Children.front(); }
    NodePointer getChild(size_t index) const { return Children[index]; }

    /// Add a new node as a child of this one.
    ///
    /// \param child - should have no parent or siblings
    /// \returns child
    NodePointer addChild(NodePointer child) {
        assert(child && "adding null child!");
        Children.push_back(child);
        return child;
    }

    /// A convenience method for adding two children at once.
    void addChildren(NodePointer child1, NodePointer child2) {
        addChild(std::move(child1));
        addChild(std::move(child2));
    }
};

/// \brief Demangle the given string as a Swift symbol.
///
/// Typical usage:
/// \code
///   NodePointer aDemangledName =
/// swift::Demangler::demangleSymbolAsNode("SomeSwiftMangledName")
/// \endcode
///
/// \param mangledName The mangled string.
/// \param options An object encapsulating options to use to perform this demangling.
///
///
/// \returns A parse tree for the demangled string - or a null pointer
/// on failure.
///
NodePointer demangleSymbolAsNode(const char *mangledName, size_t mangledNameLength, const DemangleOptions &options = DemangleOptions());

inline NodePointer demangleSymbolAsNode(const std::string &mangledName, const DemangleOptions &options = DemangleOptions()) {
    return demangleSymbolAsNode(mangledName.data(), mangledName.size(), options);
}

/// \brief Demangle the given string as a Swift symbol.
///
/// Typical usage:
/// \code
///   std::string aDemangledName =
/// swift::Demangler::demangleSymbol("SomeSwiftMangledName")
/// \endcode
///
/// \param mangledName The mangled string.
/// \param options An object encapsulating options to use to perform this demangling.
///
///
/// \returns A string representing the demangled name.
///
std::string demangleSymbolAsString(const char *mangledName, size_t mangledNameLength, const DemangleOptions &options = DemangleOptions());

inline std::string demangleSymbolAsString(const std::string &mangledName, const DemangleOptions &options = DemangleOptions()) {
    return demangleSymbolAsString(mangledName.data(), mangledName.size(), options);
}

/// \brief Demangle the given string as a Swift type.
///
/// Typical usage:
/// \code
///   NodePointer aDemangledName =
/// swift::Demangler::demangleTypeAsNode("SomeSwiftMangledName")
/// \endcode
///
/// \param mangledName The mangled string.
/// \param options An object encapsulating options to use to perform this demangling.
///
///
/// \returns A parse tree for the demangled string - or a null pointer
/// on failure.
///
NodePointer demangleTypeAsNode(const char *mangledName, size_t mangledNameLength, const DemangleOptions &options = DemangleOptions());

inline NodePointer demangleTypeAsNode(const std::string &mangledName, const DemangleOptions &options = DemangleOptions()) {
    return demangleTypeAsNode(mangledName.data(), mangledName.size(), options);
}

/// \brief Demangle the given string as a Swift type mangling.
///
/// \param mangledName The mangled string.
/// \param options An object encapsulating options to use to perform this demangling.
///
///
/// \returns A string representing the demangled name.
std::string demangleTypeAsString(const char *mangledName, size_t mangledNameLength, const DemangleOptions &options = DemangleOptions());

inline std::string demangleTypeAsString(const std::string &mangledName, const DemangleOptions &options = DemangleOptions()) {
    return demangleTypeAsString(mangledName.data(), mangledName.size(), options);
}

enum class OperatorKind {
    NotOperator,
    Prefix,
    Postfix,
    Infix,
};

/// \brief Mangle an identifier using Swift's mangling rules.
void mangleIdentifier(const char *data, size_t length, OperatorKind operatorKind, std::string &out, bool usePunycode = true);

/// \brief Remangle a demangled parse tree.
///
/// This should always round-trip perfectly with demangleSymbolAsNode.
std::string mangleNode(const NodePointer &root);

/// \brief Transform the node structure to a string.
///
/// Typical usage:
/// \code
///   std::string aDemangledName =
/// swift::Demangler::nodeToString(aNode)
/// \endcode
///
/// \param Root A pointer to a parse tree generated by the demangler.
/// \param Options An object encapsulating options to use to perform this demangling.
///
/// \returns A string representing the demangled name.
///
std::string nodeToString(NodePointer Root, const DemangleOptions &Options = DemangleOptions());

struct NodeFactory {
    static NodePointer create(Node::Kind K) { return NodePointer(new Node(K)); }
    static NodePointer create(Node::Kind K, Node::IndexType Index) { return NodePointer(new Node(K, Index)); }
    static NodePointer create(Node::Kind K, llvm::StringRef Text) { return NodePointer(new Node(K, Text)); }
    static NodePointer create(Node::Kind K, std::string &&Text) { return NodePointer(new Node(K, std::move(Text))); }
    template<size_t N> static NodePointer create(Node::Kind K, const char (&Text)[N]) { return NodePointer(new Node(K, llvm::StringRef(Text))); }
};

/// A class for printing to a std::string.
class DemanglerPrinter {
public:
    DemanglerPrinter() = default;

    DemanglerPrinter &operator<<(llvm::StringRef Value) & {
        Stream.append(Value.data(), Value.size());
        return *this;
    }

    DemanglerPrinter &operator<<(char c) & {
        Stream.push_back(c);
        return *this;
    }
    DemanglerPrinter &operator<<(unsigned long long n) &;
    DemanglerPrinter &operator<<(long long n) &;
    DemanglerPrinter &operator<<(unsigned long n) & { return *this << (unsigned long long)n; }
    DemanglerPrinter &operator<<(long n) & { return *this << (long long)n; }
    DemanglerPrinter &operator<<(unsigned n) & { return *this << (unsigned long long)n; }
    DemanglerPrinter &operator<<(int n) & { return *this << (long long)n; }

    template<typename T> DemanglerPrinter &&operator<<(T &&x) && { return std::move(*this << std::forward<T>(x)); }

    std::string &&str() && { return std::move(Stream); }

private:
    std::string Stream;
};

/// Is a character considered a digit by the demangling grammar?
///
/// Yes, this is equivalent to the standard C isdigit(3), but some platforms
/// give isdigit suboptimal implementations.
static inline bool isDigit(int c) {
    return c >= '0' && c <= '9';
}

} // end namespace Demangle
} // end namespace swift

#endif // SWIFT_BASIC_DEMANGLE_H
